//>> 这里实现阻塞队列

#pragma once

#include <iostream>

#include <queue>

#include <functional>

#include <string>

#include <pthread.h>

#include <unistd.h>

namespace BQ // 作为 阻塞队列的命名空间
{
    using func_t = std::function<void()>; // 这里表示的任务函数
    static const int _max_cap = 100;      // 最大上限

    template <typename T>
    class BQqueues
    {
    private:
        std::queue<T> _queue;          // 临界资源
        pthread_mutex_t _mutex;        // 锁 资源环境的锁
        pthread_cond_t _product_cond;  // 生产者的同步队列
        pthread_cond_t _consumer_cond; // 销售者的同步队列

        int _capacity; // 阻塞队列的容量

        int _p_waitcond = 0; // 生产者等待队列的人数
        int _c_waitcond = 0; // 消费这等待队列的人数

    public:
        BQqueues(int _out_cap) : _capacity(_out_cap), _p_waitcond(0), _c_waitcond(0)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_product_cond, nullptr);
            pthread_cond_init(&_consumer_cond, nullptr); // 初始化条件变量和锁
        }
        void Equeue(const T &_data)
        {
            // 入队列的时候要锁住
            pthread_mutex_lock(&_mutex);

            // 生产数据是有条件的！ 如果满了 就不能放了
            while (IsFull() != 0) // 说明满了
            {
                std::cout << "生产者进入等待！" << std::endl;
                _p_waitcond++;
                pthread_cond_wait(&_product_cond, &_mutex); // 这段代码 只能在临界区 黑盒效应 只有在里面才能去判断这段代码
                _p_waitcond--;
                std::cout << "生产者被唤醒！" << std::endl;
            }

            //>> 生产者生产数据
            _queue.push(_data);

            //>> 唤醒一个消费者
            if (_c_waitcond)
            {
                pthread_cond_signal(&_consumer_cond);
            }

            // 使用完毕之后 解锁
            pthread_mutex_unlock(&_mutex);
        }

        void Epop(T *_out)
        {
            pthread_mutex_lock(&_mutex);

            while (IsEmpty() != 0)
            {
                std::cout << "消费者进入等待！" << std::endl;

                _c_waitcond++;
                pthread_cond_wait(&_consumer_cond, &_mutex);
                _c_waitcond--;

                std::cout << "消费者被唤醒！" << std::endl;
            }
            *_out = _queue.front();
            _queue.pop();

            //>> 唤醒生产者
            if (_p_waitcond)
            {
                pthread_cond_signal(&_product_cond);
            } // 即使被唤醒了 还要申请锁 即使没有释放锁 生产者也只是在 锁申请队伍中

            pthread_mutex_unlock(&_mutex);
        }

        ~BQqueues()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_product_cond);
            pthread_cond_destroy(&_consumer_cond);
        }

    private:
        bool IsFull()
        {
            return _queue.size() == _capacity;
        }

        bool IsEmpty()
        {
            return _queue.empty();
        }
    };

}